package cn.iocoder.yudao.module.system.service.cp;

import cn.iocoder.yudao.framework.redis.core.dao.DeptListRedisDAO;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.cp.vo.CheckinMonthDataReqVO;
import cn.iocoder.yudao.module.system.controller.admin.cp.vo.CorrectionReq;
import cn.iocoder.yudao.module.system.controller.admin.cp.vo.NoticeMessageForm;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.enums.cp.CpEnum;
import cn.iocoder.yudao.module.system.framework.cp.config.WxCpConfiguration;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.google.common.collect.Lists;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpMessageService;
import me.chanjar.weixin.cp.api.WxCpOaService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpMessageServiceImpl;
import me.chanjar.weixin.cp.api.impl.WxCpOaServiceImpl;
import me.chanjar.weixin.cp.bean.Gender;
import me.chanjar.weixin.cp.bean.WxCpDepart;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.bean.message.WxCpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
import me.chanjar.weixin.cp.bean.oa.WxCpCheckinData;
import me.chanjar.weixin.cp.bean.oa.WxCpCheckinMonthData;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author pg
 * @project zysx
 * @description 企微service实现类
 * @date 2024/2/26 10:26:09
 */
@Service
@Slf4j
public class CpServiceImpl implements CpService {
    @Resource
    private DeptService deptService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private AdminUserMapper userMapper;

    @Resource
    private AdminUserService userService;

    @Resource
    private DeptListRedisDAO deptListRedisDAO;

    // 次方法
    static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }

    @Override
    public Boolean createDept(Integer tenantId) {
        //删除所有部门的redis缓存
        deptListRedisDAO.deleteDeptListCache(Long.valueOf(tenantId));

        final WxCpService wxCpService = WxCpConfiguration.getCpService(CpEnum.valueOfTenantId(tenantId).getCorpId(), CpEnum.valueOfTenantId(tenantId).getAgentId());
        try {
            List<WxCpDepart> result = wxCpService.getDepartmentService().list(1L);
            log.info("成功获取部门个数：{}", result.size());
            List<DeptSaveReqVO> createReqVO = Lists.newArrayList();
            result.forEach(
                    wxCpDepart -> {
                        DeptSaveReqVO vo = new DeptSaveReqVO();
                        vo.setSort(Math.toIntExact(wxCpDepart.getOrder()));
                        vo.setStatus(0);
                        vo.setId(wxCpDepart.getId() + (tenantId - 1) * 1000L);
                        vo.setName(wxCpDepart.getName());
                        vo.setParentId(wxCpDepart.getParentId() + (tenantId - 1) * 1000L);
                        createReqVO.add(vo);
                    }
            );
            return deptService.createDept(createReqVO) && createUser(tenantId, -1L);
        } catch (WxErrorException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Boolean createUser(Integer tenantId, Long id) {
        final WxCpService wxCpService = WxCpConfiguration.getCpService(CpEnum.valueOfTenantId(tenantId).getCorpId(), CpEnum.valueOfTenantId(tenantId).getAgentId());
        List<AdminUserDO> userDOS = Lists.newArrayList();
        try {
            if (id == null || id < 0) {
                log.info("创建所有部门人员");
                List<WxCpDepart> departs = wxCpService.getDepartmentService().list(1L);
                for (WxCpDepart deptDO : departs) {
                    List<WxCpUser> result = wxCpService.getUserService().listByDepartment(deptDO.getId(), null, null);
//                    log.info(JsonUtils.toJsonString(result));
                    log.info("成功获取部门：{}，人员数为：{}", deptDO.getName(), result.size());
                    userDOS.addAll(converList(result, tenantId));
                }
            } else {
                List<WxCpUser> result = wxCpService.getUserService().listByDepartment(id-((tenantId - 1) * 1000L), null, null);
                userDOS.addAll(converList(result, tenantId));
            }
            log.info("过滤前用户人数：{}", userDOS.size());
            userDOS = userDOS.stream().filter(distinctByKey(AdminUserDO::getUsername)).collect(Collectors.toList());
            log.info("开始插入用户人数：{}", userDOS.size());
            List<AdminUserDO> oldUsers;
            if (id == null || id < 0) {
                oldUsers = userMapper.selectList();
            } else {
                oldUsers = userService.getUserListByDeptIds(Collections.singletonList(id));
            }
            oldUsers = oldUsers.stream().filter(distinctByKey(AdminUserDO::getUsername)).filter(x -> x.getId() > 2).collect(Collectors.toList());
            syncUsers(userDOS, oldUsers);
            return true;
        } catch (WxErrorException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public WxCpMessageSendResult sendMessage(Integer tenantId, NoticeMessageForm form) throws WxErrorException {
        WxCpService wxCpService = WxCpConfiguration.getCpService(CpEnum.valueOfTenantId(tenantId).getCorpId(), CpEnum.valueOfTenantId(tenantId).getAgentId());
        WxCpMessageService wxCpMessageService = new WxCpMessageServiceImpl(wxCpService);
        WxCpMessage wxCpMessage = new WxCpMessage();
        wxCpMessage.setToUser(form.getToUser());
        wxCpMessage.setMsgType("miniprogram_notice");
        wxCpMessage.setAppId("wx9d6e46427f2627ba");
        wxCpMessage.setPage("pages/message/message?isform=ture&tenantid=" + tenantId + "&cropId=" + CpEnum.valueOfTenantId(tenantId).getCorpId() + "&agentId=" + CpEnum.valueOfTenantId(tenantId).getAgentId());
        wxCpMessage.setTitle(form.getTitle());
//        wxCpMessage.setDescription("4月27日 16:16");
//        wxCpMessage.setEmphasisFirstItem(true);
        Map<String, String> contentItems = new HashMap<>();
//        contentItems.put("会议室","111");
//        contentItems.put("会议地点","广州TIT-402会议室");
//        contentItems.put("会议时间","2018年8月1日 09:00-09:30");
//        contentItems.put("参与人员","彭");
        wxCpMessage.setContentItems(contentItems);
        return wxCpMessageService.send(wxCpMessage);
    }

    // 获取打卡记录数据
    @Override
    public List<WxCpCheckinData> getCheckInData(Integer tenantId, Integer openCheckinDataType, Date startTime, Date endTime, List<String> userIdList) throws WxErrorException {
        WxCpService wxCpService = WxCpConfiguration.getCpService(CpEnum.valueOfTenantId(tenantId).getCorpId(), CpEnum.valueOfTenantId(tenantId).getAgentId());
        WxCpOaService wxCpOaService = new WxCpOaServiceImpl(wxCpService);
        List<WxCpCheckinData> wxCpCheckinDatas = wxCpOaService.getCheckinData(openCheckinDataType, startTime, endTime, userIdList);
        return wxCpCheckinDatas;
    }

    @Override
    public List<WxCpCheckinMonthData> getCheckinMonthData(Integer tenantId, CheckinMonthDataReqVO reqVO) throws WxErrorException {
        WxCpService wxCpService = WxCpConfiguration.getCpService(CpEnum.valueOfTenantId(tenantId).getCorpId(), CpEnum.valueOfTenantId(tenantId).getAgentId());
        WxCpOaService wxCpOaService = new WxCpOaServiceImpl(wxCpService);
        List<WxCpCheckinMonthData> wxCpCheckinMonthData = wxCpOaService.getCheckinMonthData(reqVO.getStartTime(), reqVO.getEndTime(), Collections.singletonList(reqVO.getUserId()));
        return wxCpCheckinMonthData;
    }

    @Override
    public void punchCorrection(Integer tenantId, CorrectionReq req) throws WxErrorException {
        WxCpService wxCpService = WxCpConfiguration.getCpService(CpEnum.valueOfTenantId(tenantId).getCorpId(), CpEnum.valueOfTenantId(tenantId).getAgentId());
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("userid", req.getUserId());
        jsonObject.addProperty("schedule_date_time", req.getScheduleDateTime());
        jsonObject.addProperty("checkin_time", req.getCheckinTime());
        jsonObject.addProperty("schedule_checkin_time", req.getScheduleCheckinTime());

        String url = wxCpService.getWxCpConfigStorage().getApiUrl("/cgi-bin/checkin/punch_correction");
        String responseContent = wxCpService.post(url, jsonObject.toString());
        log.info("responseContent:{}", responseContent);
//        return (WxCpApprovalDetailResult) WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDetailResult.class);
    }

    private List<AdminUserDO> converList(List<WxCpUser> result, Integer tenantId) {
        List<AdminUserDO> userDOS = Lists.newArrayList();
        result.forEach(
                wxCpUser -> {
                    AdminUserDO adminUserDO = new AdminUserDO();
                    adminUserDO.setMobile(wxCpUser.getMobile());
                    adminUserDO.setUsername(wxCpUser.getUserId().toLowerCase());
                    adminUserDO.setPassword(encodePassword("zcsx123"));
                    adminUserDO.setDeptId(wxCpUser.getDepartIds()[0] + (tenantId - 1) * 1000L);
                    adminUserDO.setStatus(0);
                    adminUserDO.setNickname(wxCpUser.getName());
                    adminUserDO.setEmail(wxCpUser.getEmail());
                    adminUserDO.setAvatar(wxCpUser.getAvatar());
                    adminUserDO.setSex(getGen(wxCpUser.getGender()));
                    adminUserDO.setWxUserId(wxCpUser.getUserId());
                    userDOS.add(adminUserDO);
                }
        );
        return userDOS;
    }

    private Integer getGen(Gender gender) {
        if (gender == null) {
            return 3;
        }
        if ("MALE".equals(gender.toString())) {
            return 0;
        } else if ("FEMALE".equals(gender.toString())) {
            return 1;
        }
        return 3;
    }

    /**
     * 对密码进行加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    private void syncUsers(List<AdminUserDO> newUsers, List<AdminUserDO> oldUsers) {
        Map<String, AdminUserDO> newUsersMap = newUsers.stream().collect(Collectors.toMap(AdminUserDO::getWxUserId, Function.identity()));
        Map<String, AdminUserDO> oldUsersMap = oldUsers.stream().collect(Collectors.toMap(AdminUserDO::getWxUserId, Function.identity()));
        // 移除的用户
        List<AdminUserDO> deleteList = Lists.newArrayList();
        // 新增的用户
        List<AdminUserDO> addList = Lists.newArrayList();
        newUsersMap.forEach((key, value) -> {
            if (!oldUsersMap.containsKey(key)) {
                addList.add(value);
            }
        });
        oldUsersMap.forEach((key, value) -> {
            if (!newUsersMap.containsKey(key)) {
                deleteList.add(value);
            }
        });
        if (!CollectionUtils.isEmpty(deleteList)) {
            userMapper.deleteBatchIds(deleteList.stream().map(AdminUserDO::getId).collect(Collectors.toList()));
        }
        if (!CollectionUtils.isEmpty(addList)) {
            userMapper.insertOrUpdateBatch(addList);
        }
    }
}
